defmodule SockWorkstation.ASRHelperGen do
  use GenServer

  alias SockWorkstation.{CoopMgrGen,PingPongGen}
  # 1 MB 最大缓冲区大小
  # @max_buffer_size 1024 * 1024 * 1
  @name __MODULE__
  # 200 毫秒发送间隔
  # @send_interval 60

  # Client API
  def start_link(session_id) do
    GenServer.start_link(@name, session_id, name: via_tuple(session_id, "asr_helper"))
  end

  @impl true
  def init(session_id) do
    self_pid = self()
    CoopMgrGen.fetch_asr_helper_pid(session_id, self_pid)
    # GrpcVADClient.fetch_asr_helper_pid(session_id, self_pid)
    start_asr_pipe()
    # start_sent_asr(session_id)
    # send(self_pid, :run_sent_asr)

    {:ok,
     %{
       #  bin_buffer: <<>>,
       session_id: session_id,
       self_pid: self_pid,
      #  is_inited?: true,
       asr_type: :asr,
      #  auto_listen?: false,
       coop_pid: nil,
       #  timer_ref: nil,
       asr_started?: false
       #  chunk_num: 0
     }}
  end

  def get_state(session_id) do
    GenServer.call(via_tuple(session_id, "asr_helper"), :get_state)
  end
  def fetch_asr_helper_pid(session_id) do
    GenServer.call(via_tuple(session_id, "asr_helper"), :fetch_asr_helper_pid)
  end

  def start_asr_pipe() do
    SockWorkstation.ASRHelperGen.ASRPipeline.start()
  end

  def starting_asr(session_id, asr_pid) do
    GenServer.cast(via_tuple(session_id, "asr_helper"), {:asr_starting, asr_pid})
  end

  def toggle_vad(session_id) do
    GenServer.cast(via_tuple(session_id, "asr_helper"), :toggle_vad)
  end
  def toggle_asr(session_id) do
    GenServer.cast(via_tuple(session_id, "asr_helper"), :toggle_asr)
  end

  def fetch_coop_pid(session_id, pid) do
    GenServer.cast(via_tuple(session_id, "asr_helper"), {:fetch_coop_pid, pid})
  end

  # def rm_coop_pid(session_id) do
  #   GenServer.cast(via_tuple(session_id, "asr_helper"), :rm_coop_pid)
  # end

  def stopping_asr(session_id) do
    GenServer.cast(via_tuple(session_id, "asr_helper"), :asr_stopping)
  end

  # def start_sent_asr(session_id) do
  # GenServer.cast(via_tuple(session_id, "asr_helper"), :run_sent_asr)
  # end

  def frsent_opus(session_id, opus_data) do
    GenServer.cast(via_tuple(session_id, "asr_helper"), {:frsent_opus, opus_data})
  end

  @impl true
  def handle_call(:get_state, _from, state) do
    {:reply, state, state}

  end

    def handle_call(:fetch_asr_helper_pid, _from, state) do
    {:reply, {:ok,state.self_pid}, state}
  end
  @impl true
  def handle_info(:run_sent_asr, state) do
    IO.puts("run_sent_asr")

    # if state.is_inited? == false,
    #   do: SockWorkstation.SockCoopClient.send_listening(state.coop_pid, state.session_id)

    CoopMgrGen.toggle_listening(state.session_id)
    {:ok, asr_pid} =
      SockWorkstation.CoopHelperSup.super_asr_client(state.session_id)

    # Process.send_after(state.self_pid, :asr_started, 250)

    state =
      state
      |> Map.put(:asr_pid, asr_pid)
      # |> Map.put(:is_inited?, false)

    {:noreply, state}
  end

  def handle_info(:vad_done, state) do
    IO.puts("vad done #{state.session_id}")
    Process.sleep(500)
    PingPongGen.switch_totalk(state.session_id)
    {:noreply, state}
  end
  @impl true
  def handle_cast({:fetch_coop_pid, coop_pid}, state) when coop_pid == state.coop_pid do
    {:noreply, state}
  end

  def handle_cast({:fetch_coop_pid, coop_pid}, state) do
    {:noreply, %{state | coop_pid: coop_pid}}
  end

  def handle_cast({:frsent_opus, opus_data}, state) when state.asr_type == :vad do
    # IO.puts("recv_opus #{byte_size(opus_data)}")
    # {:ok, pcm_data} = SockWorkstation.CodecNifGen.decode_opus(state.session_id, opus_data)
    # SockWorkstation.GrpcVADClient.send_pcm(state.session_id, pcm_data)
    # File.write!("test.pcm", data, [:append])
     SockWorkstation.ASRHelperGen.ASRPipeline.handle_event(
      {:vad_opuschunk, state.session_id, state.self_pid, opus_data}
    )

    {:noreply, state}
  end

  def handle_cast({:frsent_opus, opus_data}, state) when is_pid(state.asr_pid) do
    # IO.puts("Sending buffer data to processor: #{byte_size(opus_data)} bytes")
    # if state.chunk_num > 0 do
    # pcm_data = send_to_processor(state.session_id, state.bin_buffer)
    # SockWorkstation.SockASRClient.send_asr_task(state.asr_pid, pcm_data)
    SockWorkstation.ASRHelperGen.ASRPipeline.handle_event(
      {:asr_opuschunk, state.session_id, state.asr_pid, opus_data}
    )

    # File.write!("cache.pcm", pcm_data, [:append])
    # end

    {:noreply, state}
    # {:noreply, %{state | bin_buffer: bin_buffer, chunk_num: state.chunk_num + 1}}
  end


  def handle_cast({:frsent_opus, _opus_data}, state) do
    # IO.puts("Sending buffer data to processor: #{byte_size(opus_data)} bytes")
    {:noreply, state}
  end



  def handle_cast({:asr_starting, pid}, state) do
    {:noreply, %{state | asr_starting: true, asr_pid: pid}}
  end

  def handle_cast(:asr_stopping, state) do
    {:noreply, %{state | asr_starting: false, asr_pid: nil}}
  end
  def handle_cast(:toggle_vad, state) when state.asr_type == :asr do
    {:noreply, %{state | asr_type: :vad}}
  end
    def handle_cast(:toggle_vad, state)  do
    {:noreply, state}
  end
  def handle_cast(:toggle_asr, state) when state.asr_type == :vad do
    {:noreply, %{state | asr_type: :asr}}
  end
  def handle_cast(:toggle_asr, state) do
    {:noreply, %{state | asr_type: :asr}}
  end

  # def handle_cast(:rm_coop_pid, state) do
  #   {:noreply, %{state | coop_pid: nil}}
  # end

  # @impl true
  # def terminate(_reason, state) do
  #   # 清理资源
  #   CoopMgrGen.rm_asr_helper_pid(state.session_id)
  #   :ok
  # end

  defp via_tuple(session_id, type) do
    {:via, Registry, {SockWorkstation.SessionRegistry, {session_id, type}}}
  end
end

defmodule SockWorkstation.ASRHelperGen.ASRPipeline do
  use ALF.DSL

  @components [
    stage(:source_to_processor),
    stage(:sink_to_asr)
  ]

  def handle_event(event) do
    case event do
      {:asr_opuschunk, session_id, coop_pid, opus_data} ->
        with {:ok, {:pcm_data, session_id, coop_pid, pcm_data}} <-
               source_to_processor({:opus_data, session_id, coop_pid, opus_data}, %{}),
             {:ok, _session_id} <- sink_to_asr({:pcm_data, session_id, coop_pid, pcm_data}, %{}) do
          # 处理结果
          :ok
        end
      {:vad_opuschunk, session_id, coop_pid, opus_data} ->
        with {:ok, {:pcm_data, session_id, coop_pid, pcm_data}} <-
               source_to_processor({:opus_data, session_id, coop_pid, opus_data}, %{}),
             {:ok, _session_id} <- sink_to_vad({:pcm_data, session_id, coop_pid, pcm_data}, %{}) do
          # 处理结果
          :ok
        end
    end
  end

  def source_to_processor({:opus_data, session_id, asr_pid, opus_data}, _opts) do
    case SockWorkstation.CodecNifGen.decode_opus(session_id, opus_data) do
      {:ok, pcm_data} ->
        {:ok, {:pcm_data, session_id, asr_pid, pcm_data}}

      {:error, reason} ->
        {:error, reason}
    end
  end

  def sink_to_asr({:pcm_data, session_id, asr_pid, pcm_data}, _opts) do
    case SockWorkstation.SockASRClient.send_asr_task(asr_pid, pcm_data) do
      :ok ->
        {:ok, session_id}

      {:error, reason} ->
        {:error, reason}
    end
  end

  def sink_to_vad({:pcm_data, session_id, _asr_pid, pcm_data}, _opts) do
    with :ok <- SockWorkstation.GrpcVADClient.send_pcm(session_id, pcm_data) do
      # :ok ->
        # {:ok, session_id}

      # {:error, reason} ->
      #   {:error, reason}
    end
  end
end

# bin_buffer = <<state.bin_buffer::binary, opus_data::binary>>
# pcm_data = send_to_processor(state.session_id, state.bin_buffer)

# if byte_size(state.bin_buffer) > 0,
# do: IO.puts("Sending buffer data to processor: #{byte_size(state.bin_buffer)} bytes")

# pcm_data = send_to_processor(state.session_id, state.bin_buffer)
# SockWorkstation.SockASRClient.send_asr_task(state.asr_pid, pcm_data)
# defp log_unknown_event(event) do
#   IO.puts("evet: #{inspect(event, pretty: true, width: 80)}")
# end

# defp log_error(error) do
#   IO.puts("ASRPipeline error: #{inspect(error)}")
# e

# def push_data(session_id, data) do
#   GenServer.cast(via_tuple(session_id, "asr_helper"), {:opus_data, data})
# end

# def get_state(session_id) do
#   GenServer.call(via_tuple(session_id, "asr_helper"), :get_state)
# end

# def clear_buffer(session_id) do
#   GenServer.cast(via_tuple(session_id, "asr_helper"), :clear_buffer)
# end

# Server Callbacks

# @impl true
# def handle_call(:get_state, _from, state) do
#   {:reply, state, state}
# end

# def send_to_processor(session_id, opus_data) do
#   case SockWorkstation.CodecNifGen.decode_opus(session_id, opus_data) do
#     {:ok, pcm_data} ->
#       {:ok, pcm_data}

#     {:error, reason} ->
#       IO.puts("Error decoding Opus data: #{reason}")
#       {:error, reason}
#   end
# end

# def send_to_asr(asr_pid, pcm_data) do
#   case SockWorkstation.SockASRClient.send_asr_task(asr_pid, pcm_data) do
#     :ok ->
#       :ok

#     {:error, reason} ->
#       IO.puts("Error sending PCM data to ASR: #{reason}")
#       {:error, reason}
#   end
# end
# end

# def handle_info(:asr_started, state) when state.asr_starting == true do
# schedule_send(@send_interval, state.self_pid)

# if byte_size(state.bin_buffer) > 0,
#   do: IO.puts("Sending buffer data to processor: #{byte_size(state.bin_buffer)} bytes")

# # pcm_data = send_to_processor(state.session_id, state.bin_buffer)
# # SockWorkstation.SockASRClient.send_asr_task(state.asr_pid, pcm_data)
# if state.chunk_num > 0 do
#   # pcm_data = send_to_processor(state.session_id, state.bin_buffer)
#   # SockWorkstation.SockASRClient.send_asr_task(state.asr_pid, pcm_data)
#   SockWorkstation.ASRHelperGen.ASRPipeline.handle_event(
#     {:opus_data, state.session_id, state.asr_pid, state.bin_buffer}
#   )

#   # File.write!("cache.pcm", pcm_data, [:append])
# end

#   {:noreply, state}
# end

# def handle_info(:asr_started, state) do
#   {:noreply, %{state | bin_buffer: <<>>, chunk_num: 0}}
# end

# def handle_cast(:clear_buffer, state) do
#   {:noreply, %{state | bin_buffer: <<>>, chunk_num: 0}}
# end

# 辅助函数：设置定时器
# defp schedule_send(interval, pid) do
#   Process.send_after(pid, :asr_started, interval)
# end

# def handle_info({:sent_opus, opus_data}, state) do
#   # IO.puts("Sending buffer data to processor: #{byte_size(opus_data)} bytes")
#   # pcm_data = send_to_processor(state.session_id, opus_data)
#   bin_buffer = <<state.bin_buffer::binary, opus_data::binary>>
#   # pcm_data = send_to_processor(state.session_id, state.bin_buffer)
#   # 检查缓冲区大小，如果超过限制则处理并清空
#   # if byte_size(bin_buffer) >= @max_buffer_size do
#     # 发送缓冲区数据到处理模块
#     # send_to_processor(bin_buffer)
#     # 清空缓冲区
#     # {:noreply, %{state | bin_buffer: <<>>, chunk_num: 0}}
#   # else
#     # 更新缓冲区
#     {:noreply, %{state | bin_buffer: bin_buffer, chunk_num: state.chunk_num + 1}}
#   # end
# end

# def handle_info({:asr_starting, pid}, state) do
#   {:noreply, %{state | asr_starting: true, bin_buffer: <<>>, chunk_num: 0, asr_pid: pid}}
# end

# def handle_cast(:run_sent_asr, state) do
#   # SockWorkstation.SockCoopClient.send_listening(state["self_pid"])
#   IO.puts("run_sent_asr")
#   if state.is_inited == false, do: SockWorkstation.SockCoopClient.send_listening(CoopMgrGen.start_link(state.session_id).coop_pid)

#   {:ok, asr_pid} =
#     SockWorkstation.CoopHelperSup.start_asr_client(state.session_id)

#   Process.send_after(state.self_pid, :asr_started, 250)

#   state =
#     state
#     |> Map.put(:asr_pid, asr_pid)
#     # |> Map.put(:asr_task_id, asr_task_id)
#     |> Map.put(:is_inited, false)

#   # |> Map.delete(:tts_task_id)
#   {:noreply, state}
# end

# def handle_info({:test_opus, opus}, state) do
#   # SockWorkstation.SockCoopClient.send_binary(CoopMgrGen.get_state(state.session_id).coop_pid, opus)
#   {:noreply, state}
# end

# def handle_cast({:sent_opus, opus_data}, state) do
#   # IO.puts("Sending buffer data to processor: #{byte_size(opus_data)} bytes")
#   # pcm_data = send_to_processor(state.session_id, opus_data)
#   bin_buffer = <<state.bin_buffer::binary, opus_data::binary>>
#   # pcm_data = send_to_processor(state.session_id, state.bin_buffer)
#   # 检查缓冲区大小，如果超过限制则处理并清空
#   # if byte_size(bin_buffer) >= @max_buffer_size do
#     # 发送缓冲区数据到处理模块
#     # send_to_processor(bin_buffer)
#     # 清空缓冲区
#     # {:noreply, %{state | bin_buffer: <<>>, chunk_num: 0}}
#   # else
#     # 更新缓冲区
#     {:noreply, %{state | bin_buffer: bin_buffer, chunk_num: state.chunk_num + 1}}
#   # end
# end
